package com.kob.backend.consumer;

import com.alibaba.fastjson.JSONObject;
import com.kob.backend.consumer.utils.Game;
import com.kob.backend.consumer.utils.JwtAuthentication;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
//@ServerEndpoint注解用于将一个普通Java类定义为WebSocket服务端端点。被这个注解标记的类，在WebSocket连接建立时，
//Spring容器并不会像管理普通 @Component或 @Service那样进行完整的生命周期管理
@ServerEndpoint("/websocket/{token}")  // 注意不要以'/'结尾
public class WebSocketServer {
    private Session session = null;//session是websocket里的一个包
    private User user;//用户信息应该存储到每个session里面
    //被static修饰的变量称为静态变量，无论创建多少个类的实例，静态变量在内存中只有一份，如果不加static修饰，则每个实例都会创建一个变量
    //这个哈希表是要在每个实例里面都访问同一个哈希表，所以要用static修饰

    //存储所有链接，比如从匹配系统接收到匹配成功的信息后，要将匹配成功的信息发送给两名玩家，需要根据用户的id找到他所对应的链接是谁，
    //才可以利用这个链接向前端发起请求，所以要定义一个变量存储所有的链接
    //ConcurrentHashMap是线程安全的哈希表，键是Integer类型，值是WebSocketServer类型，
    //用于在 WebSocket 服务中管理 “用户 ID” 和 “WebSocket 服务端对象” 的对应关系
    final private static ConcurrentHashMap<Integer, WebSocketServer> users = new ConcurrentHashMap<>();
    //在后端需要建立一个线程安全的Set作为匹配池
    final private static CopyOnWriteArraySet<User> matchPool = new CopyOnWriteArraySet<>();
    //由于websocket是多例的，所以不能用普通的注入方式，还要设置成static
    private static UserMapper userMapper;
    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        WebSocketServer.userMapper = userMapper;
    }
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) throws IOException {
        // 建立连接
        this.session = session;//建立连接时，需要将session存下来
        System.out.println("connected");
        Integer userId = JwtAuthentication.getUserId(token);//获取用户id
        this.user = userMapper.selectById(userId);//获取用户信息
        if(this.user!=null){
            users.put(userId, this);//this在这里的核心意义是指代当前WebSocket连接对应的实例对象
        }else{
            this.session.close();
        }
        System.out.println(users);
    }

    @OnClose
    public void onClose() {
        // 关闭链接
        System.out.println("disconnected");
        if(this.user!=null){
            users.remove(this.user.getId());//删除用户
            matchPool.remove(this.user);
        }
    }
    private void startMatching() {
        System.out.println("start matching");
        matchPool.add(this.user);
        while(matchPool.size()>=2){//匹配池中至少有两个用户
            Iterator<User> it = matchPool.iterator();
            User a = it.next(), b = it.next();
            matchPool.remove(a);//匹配成功后就将这两个用户删除了
            matchPool.remove(b);
            //这里地图是匹配成功的链接，但不一定是A和B的链接，所以要存到A和B里面
            Game game = new Game(13, 12, 20);//生成地图
            game.createMap();//初始化地图
            //分别给user1和user2传送消息告诉他们匹配成功了
            //通过user1的连接向user1发消息
            JSONObject respA = new JSONObject();
            respA.put("event", "start-matching");
            respA.put("opponent_username", b.getUsername());//对手信息
            respA.put("opponent_photo", b.getPhoto());
            respA.put("gamemap", game.getG());
            users.get(a.getId()).sendMessage(respA.toJSONString());//获取a的链接，将信息发送给前端
            //通过user2的连接向user2发消息
            JSONObject respB = new JSONObject();
            respB.put("event", "start-matching");
            respB.put("opponent_username", a.getUsername());//对手信息
            respB.put("opponent_photo", a.getPhoto());
            respB.put("gamemap", game.getG());
            users.get(b.getId()).sendMessage(respB.toJSONString());//获取b的链接，将信息发送给前端
        }
    }
    private void stopMatching() {
        System.out.println("stop matching");
        matchPool.remove(this.user);
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        // 从Client接收消息（后端接收到前端的信息）
        System.out.println("receive message");
        JSONObject data = JSONObject.parseObject(message);//将符串解析成json
        String event = data.getString("event");//接收前端传过来的event
        if("start-matching".equals(event)){//前面写start_matching，这样避免event为空抛异常
            startMatching();
        }else{
            stopMatching();
        }
    }
    public void sendMessage(String message) {
        // 给Client发送消息（后端发送给前端的信息）
        //WebSocket是异步通信的，通过 synchronized (this.session) 加锁，保证同一时刻仅有一个线程操作 session 发送消息，
        //避免多线程并发下 session 操作混乱
        synchronized (this.session){
            try {
                this.session.getBasicRemote().sendText(message);//将信息发送给前端了
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }
}
